package main

import "fmt"
import "reflect"

/*
reflect.Value类型和值
类似的，我们可以通过调用reflect.ValueOf函数，从一个非接口类型的值创建一个reflect.Value值。 此reflect.Value值代表着此非接口值。
和reflect.TypeOf函数类似，reflect.ValueOf函数也只有一个interface{}类型的参数。
当我们将一个接口值传递给一个reflect.ValueOf函数调用时，此调用返回的是代表着此接口值的动态值的一个reflect.Value值。
我们必须通过间接的途径获得一个代表一个接口值的reflect.Value值。

被一个reflect.Value值代表着的值常称为此reflect.Value值的底层值（underlying value）。

reflect.Value类型有很多方法。 我们可以调用这些方法来观察和操纵一个reflect.Value属主值表示的Go值。
这些方法中的有些适用于所有种类类型的值，有些只适用于一种或几种类型的值。
通过不合适的reflect.Value属主值调用某个方法将在运行时产生一个恐慌。 请阅读reflect代码库中各个方法的文档来获取如何正确地使用这些方法。

一个reflect.Value值的CanSet方法将返回此reflect.Value值代表的Go值是否可以被修改（可以被赋值）。
如果一个Go值可以被修改，则我们可以调用对应的reflect.Value值的Set方法来修改此Go值。
注意：reflect.ValueOf函数直接返回的reflect.Value值都是不可修改的。
*/
func main() {
	n := 123
	p := &n
	vp := reflect.ValueOf(p)
	//CanAddr reports whether the value's address can be obtained with Addr.
	fmt.Println("vp.CanSet(), vp.CanAddr():", vp.CanSet(), vp.CanAddr()) // false false
	vn := vp.Elem()
	fmt.Println("vn.Type():", vn.Type()) //
	fmt.Println("vn:", vn)               //
	fmt.Println("vn.Int():", vn.Int())   // 123
	// 取得vp的底层指针值引用的值的代表值
	fmt.Println("vn.CanSet(), vn.CanAddr():", vn.CanSet(), vn.CanAddr()) // true true
	vn.Set(reflect.ValueOf(789))                                         // <=> vn.SetInt(789)
	fmt.Println("n:", n)                                                 // 789
	fmt.Println("vn.Int():", vn.Int())                                   // 789

}

//一个结构体值的非导出字段不能通过反射来修改。
//
func DemoUnexportedField() {
	var s struct {
		X interface{} // 一个导出字段
		y interface{} // 一个非导出字段
	}
	vp := reflect.ValueOf(&s)
	// 如果vp代表着一个指针，下一行等价于"vs := vp.Elem()"。
	vs := reflect.Indirect(vp)
	// vx和vy都各自代表着一个接口值。
	vx, vy := vs.Field(0), vs.Field(1)
	fmt.Println("vx.CanSet(), vx.CanAddr():", vx.CanSet(), vx.CanAddr()) // true true
	// vy is addressable but not modifiable.
	fmt.Println("vy.CanSet(), vy.CanAddr():", vy.CanSet(), vy.CanAddr()) // false true
	vb := reflect.ValueOf(123)
	vx.Set(vb) // okay, 因为vx代表的值是可修改的。
	// vy.Set(vb)  // 会造成恐慌，因为vy代表的值是不可修改的。
	fmt.Println("s:", s)                                           // {123
	fmt.Println("vx.IsNil(), vy.IsNil():", vx.IsNil(), vy.IsNil()) // false true
}
